package com.leyou.search.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.item.pojo.*;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecificationClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.SearchRequest;
import com.leyou.search.pojo.SearchResult;
import com.leyou.search.repository.GoodsRepository;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchService {
    @Autowired
    private CategoryClient categoryClient;
    @Autowired
    private BrandClient brandClient;
    @Autowired
    private GoodsClient goodsClient;
    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private SpecificationClient specificationClient;
    private static final ObjectMapper MAPPER=new ObjectMapper();

    /**
     * 生成es的goods对象
     * @param spu
     * @return
     * @throws IOException
     */
    public Goods buildGoods(Spu spu) throws IOException {
        //获取分类名称
        List<String> names = categoryClient.queryNamesByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
        //获取品牌名称
        Brand brand = brandClient.queryBrandById(spu.getBrandId());
        //获取spu对应的所有价格
        List<Sku> skus = goodsClient.querySkusBySpuId(spu.getId());
        ArrayList<Long> prices = new ArrayList<>();
        //收集sku的必要字段
        List<Map<String,Object>> skuMapList=new ArrayList<>();
        skus.forEach(sku -> {
            prices.add(sku.getPrice());

            Map<String,Object> map=new HashMap<>();
            map.put("id",sku.getId());
            map.put("title",sku.getTitle());
            map.put("price",sku.getPrice());
            //获取每个sku的第一张图片
            map.put("image",StringUtils.isBlank(sku.getImages()) ? "":sku.getImages().split(",")[0]);

            skuMapList.add(map);
        });
        //获取规格参数
        List<SpecParam> specParams = specificationClient.queryParams(null, spu.getCid3(), null, true);
        //获取supDetail
        SpuDetail spuDetail = goodsClient.querySpuDetailBySpuId(spu.getId());
        //GenericSpec参数序列化为map集合
        Map<String, Object> genericSpecMap = MAPPER.readValue(spuDetail.getGenericSpec(), new TypeReference<Map<String, Object>>() {});
        //SpecialSpec参数序列化为map集合
        Map<String, List<Object>> specialSpecMap = MAPPER.readValue(spuDetail.getSpecialSpec(), new TypeReference<Map<String, List<Object>>>() {});
        //封装成map
        Map<String, Object> specs=new HashMap<>();
        specParams.forEach(specParam -> {
            //判断规格参数是否通用
            if(specParam.getGeneric()){
                //如果是通用的从genericSpecMap中取值
                String value = genericSpecMap.get(specParam.getId().toString()).toString();
                //如果是数值类型，返回区间
                if(specParam.getNumeric()){
                    value=chooseSegment(value,specParam);
                }
                specs.put(specParam.getName(),value);
            }else {
                //不是通用的从specialSpecMap中取值
                List<Object> value= specialSpecMap.get(specParam.getId().toString());
                specs.put(specParam.getName(),value);
            }
        });
        Goods goods = new Goods();
        goods.setId(spu.getId());
        goods.setBrandId(spu.getBrandId());
        goods.setCid1(spu.getCid1());
        goods.setCid2(spu.getCid2());
        goods.setCid3(spu.getCid3());
        goods.setCreateTime(spu.getCreateTime());
        goods.setSubTitle(spu.getSubTitle());
        //拼接all字段，需要分类名称以及品牌名称
        goods.setAll(spu.getTitle()+" "+ StringUtils.join(names," ")+" "+brand.getName());
        //spu下的所有sku的价格
        goods.setPrice(prices);
        //spu下的所有sku,并转化为json字符串
        goods.setSkus(MAPPER.writeValueAsString(skuMapList));
        //获取所有查询的规格参数
        goods.setSpecs(specs);
        return goods;
    }

    private String chooseSegment(String value, SpecParam p) {
        double val = NumberUtils.toDouble(value);
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = NumberUtils.toDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if(segs.length == 2){
                end = NumberUtils.toDouble(segs[1]);
            }
            // 判断是否在范围内
            if(val >= begin && val < end){
                if(segs.length == 1){
                    result = segs[0] + p.getUnit() + "以上";
                }else if(begin == 0){
                    result = segs[1] + p.getUnit() + "以下";
                }else{
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    public SearchResult search(SearchRequest searchRequest) {
        //对条件进行判断
        if(StringUtils.isBlank(searchRequest.getKey())){
            return null;
        }
        //构建自定义查询器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //构建查询条件
        //QueryBuilder basicBuild = QueryBuilders.matchQuery("all", searchRequest.getKey()).operator(Operator.AND);
        QueryBuilder basicBuild=buildBoolQueryBuilder(searchRequest);
        queryBuilder.withQuery(basicBuild);
        //分页
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));
        //添加结果过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));
        //添加分类和品牌的聚合
        String categoryAggName="categories";
        String brandAggName="brands";
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));
        //执行查询
        AggregatedPage<Goods> goodsPage =(AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());
        //获取聚合结果并解析
        List<Map<String,Object>> categories=getCategoryAggResult(goodsPage.getAggregation(categoryAggName));
        List<Brand> brands=getBrandAggResult(goodsPage.getAggregation(brandAggName));
        //当分类只有一个的时候，才进行规格参数的聚合
        List<Map<String,Object>> specs=null;
        if(!CollectionUtils.isEmpty(categories)&&categories.size()==1){
          specs=getParamAggResult((Long)categories.get(0).get("id"),basicBuild);
        }
        return new SearchResult(goodsPage.getTotalElements(),goodsPage.getTotalPages(),goodsPage.getContent(),categories,brands,specs);
    }

    /**
     * 构建布尔查询
     * @param searchRequest
     * @return
     */
    private QueryBuilder buildBoolQueryBuilder(SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //构建基本查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()).operator(Operator.AND));
        //添加过滤条件
        //获取用户选择的过滤信息
        Map<String, Object> filter = searchRequest.getFilter();
        for (Map.Entry<String, Object> entry : filter.entrySet()) {
            String key = entry.getKey();
            if(StringUtils.equals("品牌",key)){
                key="brandId";
            }else if(StringUtils.equals("分类",key)){
                key="cid3";
            }else{
                key="specs."+key+".keyword";
            }
            boolQueryBuilder.filter(QueryBuilders.termQuery(key,entry.getValue()));
        }
        return boolQueryBuilder;

    }

    /**
     * 根据查询条件聚合规格参数
     * @param cid
     * @param basicBuild
     * @return
     */
    private List<Map<String, Object>> getParamAggResult(Long cid, QueryBuilder basicBuild) {
        //构建自定义查询器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //构建基本查询
        queryBuilder.withQuery(basicBuild);
        //查询要聚合的规格参数
        List<SpecParam> params=specificationClient.queryParams(null,cid,null,true);
        //添加规格参数的聚合
        params.forEach(param->{
            queryBuilder.addAggregation(AggregationBuilders.terms(param.getName()).field("specs."+param.getName()+".keyword"));
        });
        //添加结果集过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},null));
        //执行查询
        AggregatedPage<Goods> goodsPage=(AggregatedPage<Goods>)goodsRepository.search(queryBuilder.build());
        //解析聚合结果集
        List<Map<String, Object>> specs=new ArrayList<>();
        Map<String, Aggregation> aggregationMap = goodsPage.getAggregations().asMap();
        for (Map.Entry<String, Aggregation> entry : aggregationMap.entrySet()) {
            Map<String,Object> map=new HashMap<>();
            List<String> options=new ArrayList<>();
            map.put("k",entry.getKey());
            //获取聚合
            StringTerms terms = (StringTerms)entry.getValue();
            terms.getBuckets().forEach(bucket -> {
                options.add(bucket.getKeyAsString());
            });
            map.put("options",options);
            specs.add(map);
        }
        return specs;
    }

    /**
     * 解析品牌聚合结果
     * @param aggregation
     * @return
     */
    private List<Brand> getBrandAggResult(Aggregation aggregation) {
        //转换类型为LongTerms
        LongTerms terms=(LongTerms)aggregation;
        //获得一个新的数组
        List<Brand> list=terms.getBuckets().stream().map(bucket -> {
            Long l = bucket.getKeyAsNumber().longValue();
            Brand brand = brandClient.queryBrandById(l);
            return brand;
        }).collect(Collectors.toList());
        return list;
    }

    /**
     * 分类聚合结果
     * @param aggregation
     * @return
     */
    private List<Map<String, Object>> getCategoryAggResult(Aggregation aggregation) {
        //转换类型为LongTerms
        LongTerms terms=(LongTerms)aggregation;
        List<Map<String, Object>> list = terms.getBuckets().stream().map(bucket -> {
            //初始化一个map集合
            Map<String, Object> map = new HashMap<>();
            Long id = bucket.getKeyAsNumber().longValue();
            //通过分类id查询分类名称
            List<String> names = categoryClient.queryNamesByIds(Arrays.asList(id));
            //将id和名称放入map中
            map.put("id", id);
            map.put("name", names.get(0));
            return map;
        }).collect(Collectors.toList());

        return list;
    }

    public void save(Long id) throws IOException {
        Spu spu = goodsClient.querySpuById(id);
        Goods goods = buildGoods(spu);
        goodsRepository.save(goods);
    }

    public void delete(Long id) {
        goodsRepository.deleteById(id);
    }
}
